1*ded733bdSJessica Paquette //===-------------- llvm-remark-size-diff/RemarkSizeDiff.cpp --------------===//
2*ded733bdSJessica Paquette //
3*ded733bdSJessica Paquette // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*ded733bdSJessica Paquette // See https://llvm.org/LICENSE.txt for license information.
5*ded733bdSJessica Paquette // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*ded733bdSJessica Paquette //
7*ded733bdSJessica Paquette //===----------------------------------------------------------------------===//
8*ded733bdSJessica Paquette ///
9*ded733bdSJessica Paquette /// \file
10*ded733bdSJessica Paquette /// Diffs instruction count and stack size remarks between two remark files.
11*ded733bdSJessica Paquette ///
12*ded733bdSJessica Paquette /// This is intended for use by compiler developers who want to see how their
13*ded733bdSJessica Paquette /// changes impact program code size.
14*ded733bdSJessica Paquette ///
15*ded733bdSJessica Paquette /// TODO: Add structured output (JSON, or YAML, or something...)
16*ded733bdSJessica Paquette ///
17*ded733bdSJessica Paquette //===----------------------------------------------------------------------===//
18*ded733bdSJessica Paquette 
19*ded733bdSJessica Paquette #include "llvm-c/Remarks.h"
20*ded733bdSJessica Paquette #include "llvm/ADT/Optional.h"
21*ded733bdSJessica Paquette #include "llvm/ADT/STLExtras.h"
22*ded733bdSJessica Paquette #include "llvm/ADT/SmallSet.h"
23*ded733bdSJessica Paquette #include "llvm/Remarks/Remark.h"
24*ded733bdSJessica Paquette #include "llvm/Remarks/RemarkParser.h"
25*ded733bdSJessica Paquette #include "llvm/Remarks/RemarkSerializer.h"
26*ded733bdSJessica Paquette #include "llvm/Support/CommandLine.h"
27*ded733bdSJessica Paquette #include "llvm/Support/Error.h"
28*ded733bdSJessica Paquette #include "llvm/Support/FileSystem.h"
29*ded733bdSJessica Paquette #include "llvm/Support/FormatVariadic.h"
30*ded733bdSJessica Paquette #include "llvm/Support/InitLLVM.h"
31*ded733bdSJessica Paquette #include "llvm/Support/MemoryBuffer.h"
32*ded733bdSJessica Paquette #include "llvm/Support/ToolOutputFile.h"
33*ded733bdSJessica Paquette #include "llvm/Support/WithColor.h"
34*ded733bdSJessica Paquette #include "llvm/Support/raw_ostream.h"
35*ded733bdSJessica Paquette 
36*ded733bdSJessica Paquette using namespace llvm;
37*ded733bdSJessica Paquette 
38*ded733bdSJessica Paquette enum ParserFormatOptions { yaml, bitstream };
39*ded733bdSJessica Paquette static cl::OptionCategory SizeDiffCategory("llvm-remark-size-diff options");
40*ded733bdSJessica Paquette static cl::opt<std::string> InputFileNameA(cl::Positional, cl::Required,
41*ded733bdSJessica Paquette                                            cl::cat(SizeDiffCategory),
42*ded733bdSJessica Paquette                                            cl::desc("remarks_a"));
43*ded733bdSJessica Paquette static cl::opt<std::string> InputFileNameB(cl::Positional, cl::Required,
44*ded733bdSJessica Paquette                                            cl::cat(SizeDiffCategory),
45*ded733bdSJessica Paquette                                            cl::desc("remarks_b"));
46*ded733bdSJessica Paquette static cl::opt<std::string> OutputFilename("o", cl::init("-"),
47*ded733bdSJessica Paquette                                            cl::cat(SizeDiffCategory),
48*ded733bdSJessica Paquette                                            cl::desc("Output"),
49*ded733bdSJessica Paquette                                            cl::value_desc("file"));
50*ded733bdSJessica Paquette static cl::opt<ParserFormatOptions>
51*ded733bdSJessica Paquette     ParserFormat("parser", cl::cat(SizeDiffCategory), cl::init(bitstream),
52*ded733bdSJessica Paquette                  cl::desc("Set the remark parser format:"),
53*ded733bdSJessica Paquette                  cl::values(clEnumVal(yaml, "YAML format"),
54*ded733bdSJessica Paquette                             clEnumVal(bitstream, "Bitstream format")));
55*ded733bdSJessica Paquette 
56*ded733bdSJessica Paquette /// Contains information from size remarks.
57*ded733bdSJessica Paquette // This is a little nicer to read than a std::pair.
58*ded733bdSJessica Paquette struct InstCountAndStackSize {
59*ded733bdSJessica Paquette   int64_t InstCount = 0;
60*ded733bdSJessica Paquette   int64_t StackSize = 0;
61*ded733bdSJessica Paquette };
62*ded733bdSJessica Paquette 
63*ded733bdSJessica Paquette /// Represents which files a function appeared in.
64*ded733bdSJessica Paquette enum FilesPresent { A, B, BOTH };
65*ded733bdSJessica Paquette 
66*ded733bdSJessica Paquette /// Contains the data from the remarks in file A and file B for some function.
67*ded733bdSJessica Paquette /// E.g. instruction count, stack size...
68*ded733bdSJessica Paquette struct FunctionDiff {
69*ded733bdSJessica Paquette   /// Function name from the remark.
70*ded733bdSJessica Paquette   std::string FuncName;
71*ded733bdSJessica Paquette   // Idx 0 = A, Idx 1 = B.
72*ded733bdSJessica Paquette   int64_t InstCount[2] = {0, 0};
73*ded733bdSJessica Paquette   int64_t StackSize[2] = {0, 0};
74*ded733bdSJessica Paquette 
75*ded733bdSJessica Paquette   // Calculate diffs between the first and second files.
76*ded733bdSJessica Paquette   int64_t getInstDiff() const { return InstCount[1] - InstCount[0]; }
77*ded733bdSJessica Paquette   int64_t getStackDiff() const { return StackSize[1] - StackSize[0]; }
78*ded733bdSJessica Paquette 
79*ded733bdSJessica Paquette   // Accessors for the remarks from the first file.
80*ded733bdSJessica Paquette   int64_t getInstCountA() const { return InstCount[0]; }
81*ded733bdSJessica Paquette   int64_t getStackSizeA() const { return StackSize[0]; }
82*ded733bdSJessica Paquette 
83*ded733bdSJessica Paquette   // Accessors for the remarks from the second file.
84*ded733bdSJessica Paquette   int64_t getInstCountB() const { return InstCount[1]; }
85*ded733bdSJessica Paquette   int64_t getStackSizeB() const { return StackSize[1]; }
86*ded733bdSJessica Paquette 
87*ded733bdSJessica Paquette   /// \returns which files this function was present in.
88*ded733bdSJessica Paquette   FilesPresent getFilesPresent() const {
89*ded733bdSJessica Paquette     if (getInstCountA() == 0)
90*ded733bdSJessica Paquette       return B;
91*ded733bdSJessica Paquette     if (getInstCountB() == 0)
92*ded733bdSJessica Paquette       return A;
93*ded733bdSJessica Paquette     return BOTH;
94*ded733bdSJessica Paquette   }
95*ded733bdSJessica Paquette 
96*ded733bdSJessica Paquette   FunctionDiff(StringRef FuncName, const InstCountAndStackSize &A,
97*ded733bdSJessica Paquette                const InstCountAndStackSize &B)
98*ded733bdSJessica Paquette       : FuncName(FuncName) {
99*ded733bdSJessica Paquette     InstCount[0] = A.InstCount;
100*ded733bdSJessica Paquette     InstCount[1] = B.InstCount;
101*ded733bdSJessica Paquette     StackSize[0] = A.StackSize;
102*ded733bdSJessica Paquette     StackSize[1] = B.StackSize;
103*ded733bdSJessica Paquette   }
104*ded733bdSJessica Paquette };
105*ded733bdSJessica Paquette 
106*ded733bdSJessica Paquette /// Organizes the diffs into 3 categories:
107*ded733bdSJessica Paquette /// - Functions which only appeared in the first file
108*ded733bdSJessica Paquette /// - Functions which only appeared in the second file
109*ded733bdSJessica Paquette /// - Functions which appeared in both files
110*ded733bdSJessica Paquette struct DiffsCategorizedByFilesPresent {
111*ded733bdSJessica Paquette   /// Diffs for functions which only appeared in the first file.
112*ded733bdSJessica Paquette   SmallVector<FunctionDiff> OnlyInA;
113*ded733bdSJessica Paquette 
114*ded733bdSJessica Paquette   /// Diffs for functions which only appeared in the second file.
115*ded733bdSJessica Paquette   SmallVector<FunctionDiff> OnlyInB;
116*ded733bdSJessica Paquette 
117*ded733bdSJessica Paquette   /// Diffs for functions which appeared in both files.
118*ded733bdSJessica Paquette   SmallVector<FunctionDiff> InBoth;
119*ded733bdSJessica Paquette 
120*ded733bdSJessica Paquette   /// Add a diff to the appropriate list.
121*ded733bdSJessica Paquette   void addDiff(FunctionDiff &FD) {
122*ded733bdSJessica Paquette     switch (FD.getFilesPresent()) {
123*ded733bdSJessica Paquette     case A:
124*ded733bdSJessica Paquette       OnlyInA.push_back(FD);
125*ded733bdSJessica Paquette       break;
126*ded733bdSJessica Paquette     case B:
127*ded733bdSJessica Paquette       OnlyInB.push_back(FD);
128*ded733bdSJessica Paquette       break;
129*ded733bdSJessica Paquette     case BOTH:
130*ded733bdSJessica Paquette       InBoth.push_back(FD);
131*ded733bdSJessica Paquette       break;
132*ded733bdSJessica Paquette     }
133*ded733bdSJessica Paquette   }
134*ded733bdSJessica Paquette };
135*ded733bdSJessica Paquette 
136*ded733bdSJessica Paquette static void printFunctionDiff(const FunctionDiff &FD, llvm::raw_ostream &OS) {
137*ded733bdSJessica Paquette   // Describe which files the function had remarks in.
138*ded733bdSJessica Paquette   auto FilesPresent = FD.getFilesPresent();
139*ded733bdSJessica Paquette   const auto &FuncName = FD.FuncName;
140*ded733bdSJessica Paquette   const int64_t InstDiff = FD.getInstDiff();
141*ded733bdSJessica Paquette   assert(InstDiff && "Shouldn't get functions with no size change?");
142*ded733bdSJessica Paquette   const int64_t StackDiff = FD.getStackDiff();
143*ded733bdSJessica Paquette   // Output an indicator denoting which files the function was present in.
144*ded733bdSJessica Paquette   switch (FilesPresent) {
145*ded733bdSJessica Paquette   case FilesPresent::A:
146*ded733bdSJessica Paquette     OS << "-- ";
147*ded733bdSJessica Paquette     break;
148*ded733bdSJessica Paquette   case FilesPresent::B:
149*ded733bdSJessica Paquette     OS << "++ ";
150*ded733bdSJessica Paquette     break;
151*ded733bdSJessica Paquette   case FilesPresent::BOTH:
152*ded733bdSJessica Paquette     OS << "== ";
153*ded733bdSJessica Paquette     break;
154*ded733bdSJessica Paquette   }
155*ded733bdSJessica Paquette   // Output an indicator denoting if a function changed in size.
156*ded733bdSJessica Paquette   if (InstDiff > 0)
157*ded733bdSJessica Paquette     OS << "> ";
158*ded733bdSJessica Paquette   else
159*ded733bdSJessica Paquette     OS << "< ";
160*ded733bdSJessica Paquette   OS << FuncName << ", ";
161*ded733bdSJessica Paquette   OS << InstDiff << " instrs, ";
162*ded733bdSJessica Paquette   OS << StackDiff << " stack B";
163*ded733bdSJessica Paquette   OS << "\n";
164*ded733bdSJessica Paquette }
165*ded733bdSJessica Paquette 
166*ded733bdSJessica Paquette /// Print an item in the summary section.
167*ded733bdSJessica Paquette ///
168*ded733bdSJessica Paquette /// \p TotalA - Total count of the metric in file A.
169*ded733bdSJessica Paquette /// \p TotalB - Total count of the metric in file B.
170*ded733bdSJessica Paquette /// \p Metric - Name of the metric we want to print (e.g. instruction
171*ded733bdSJessica Paquette /// count).
172*ded733bdSJessica Paquette /// \p OS - The output stream.
173*ded733bdSJessica Paquette static void printSummaryItem(int64_t TotalA, int64_t TotalB, StringRef Metric,
174*ded733bdSJessica Paquette                              llvm::raw_ostream &OS) {
175*ded733bdSJessica Paquette   OS << "  " << Metric << ": ";
176*ded733bdSJessica Paquette   int64_t TotalDiff = TotalB - TotalA;
177*ded733bdSJessica Paquette   if (TotalDiff == 0) {
178*ded733bdSJessica Paquette     OS << "None\n";
179*ded733bdSJessica Paquette     return;
180*ded733bdSJessica Paquette   }
181*ded733bdSJessica Paquette   OS << TotalDiff << " (" << formatv("{0:p}", TotalDiff / (double)TotalA)
182*ded733bdSJessica Paquette      << ")\n";
183*ded733bdSJessica Paquette }
184*ded733bdSJessica Paquette 
185*ded733bdSJessica Paquette /// Print all contents of \p Diff and a high-level summary of the differences.
186*ded733bdSJessica Paquette static void printDiffsCategorizedByFilesPresent(
187*ded733bdSJessica Paquette     DiffsCategorizedByFilesPresent &DiffsByFilesPresent,
188*ded733bdSJessica Paquette     llvm::raw_ostream &OS) {
189*ded733bdSJessica Paquette   int64_t InstrsA = 0;
190*ded733bdSJessica Paquette   int64_t InstrsB = 0;
191*ded733bdSJessica Paquette   int64_t StackA = 0;
192*ded733bdSJessica Paquette   int64_t StackB = 0;
193*ded733bdSJessica Paquette   // Helper lambda to sort + print a list of diffs.
194*ded733bdSJessica Paquette   auto PrintDiffList = [&](SmallVector<FunctionDiff> &FunctionDiffList) {
195*ded733bdSJessica Paquette     if (FunctionDiffList.empty())
196*ded733bdSJessica Paquette       return;
197*ded733bdSJessica Paquette     stable_sort(FunctionDiffList,
198*ded733bdSJessica Paquette                 [](const FunctionDiff &LHS, const FunctionDiff &RHS) {
199*ded733bdSJessica Paquette                   return LHS.getInstDiff() < RHS.getInstDiff();
200*ded733bdSJessica Paquette                 });
201*ded733bdSJessica Paquette     for (const auto &FuncDiff : FunctionDiffList) {
202*ded733bdSJessica Paquette       // If there is a difference in instruction count, then print out info for
203*ded733bdSJessica Paquette       // the function.
204*ded733bdSJessica Paquette       if (FuncDiff.getInstDiff())
205*ded733bdSJessica Paquette         printFunctionDiff(FuncDiff, OS);
206*ded733bdSJessica Paquette       InstrsA += FuncDiff.getInstCountA();
207*ded733bdSJessica Paquette       InstrsB += FuncDiff.getInstCountB();
208*ded733bdSJessica Paquette       StackA += FuncDiff.getStackSizeA();
209*ded733bdSJessica Paquette       StackB += FuncDiff.getStackSizeB();
210*ded733bdSJessica Paquette     }
211*ded733bdSJessica Paquette   };
212*ded733bdSJessica Paquette   PrintDiffList(DiffsByFilesPresent.OnlyInA);
213*ded733bdSJessica Paquette   PrintDiffList(DiffsByFilesPresent.OnlyInB);
214*ded733bdSJessica Paquette   PrintDiffList(DiffsByFilesPresent.InBoth);
215*ded733bdSJessica Paquette   OS << "\n### Summary ###\n";
216*ded733bdSJessica Paquette   OS << "Total change: \n";
217*ded733bdSJessica Paquette   printSummaryItem(InstrsA, InstrsB, "instruction count", OS);
218*ded733bdSJessica Paquette   printSummaryItem(StackA, StackB, "stack byte usage", OS);
219*ded733bdSJessica Paquette }
220*ded733bdSJessica Paquette 
221*ded733bdSJessica Paquette /// Collects an expected integer value from a given argument index in a remark.
222*ded733bdSJessica Paquette ///
223*ded733bdSJessica Paquette /// \p Remark - The remark.
224*ded733bdSJessica Paquette /// \p ArgIdx - The index where the integer value should be found.
225*ded733bdSJessica Paquette /// \p ExpectedKeyName - The expected key name for the index
226*ded733bdSJessica Paquette /// (e.g. "InstructionCount")
227*ded733bdSJessica Paquette ///
228*ded733bdSJessica Paquette /// \returns the integer value at the index if it exists, and the key-value pair
229*ded733bdSJessica Paquette /// is what is expected. Otherwise, returns an Error.
230*ded733bdSJessica Paquette static Expected<int64_t> getIntValFromKey(const remarks::Remark &Remark,
231*ded733bdSJessica Paquette                                           unsigned ArgIdx,
232*ded733bdSJessica Paquette                                           StringRef ExpectedKeyName) {
233*ded733bdSJessica Paquette   auto KeyName = Remark.Args[ArgIdx].Key;
234*ded733bdSJessica Paquette   if (KeyName != ExpectedKeyName)
235*ded733bdSJessica Paquette     return createStringError(
236*ded733bdSJessica Paquette         inconvertibleErrorCode(),
237*ded733bdSJessica Paquette         Twine("Unexpected key at argument index " + std::to_string(ArgIdx) +
238*ded733bdSJessica Paquette               ": Expected '" + ExpectedKeyName + "', got '" + KeyName + "'"));
239*ded733bdSJessica Paquette   int64_t Val;
240*ded733bdSJessica Paquette   auto ValStr = Remark.Args[ArgIdx].Val;
241*ded733bdSJessica Paquette   if (getAsSignedInteger(ValStr, 0, Val))
242*ded733bdSJessica Paquette     return createStringError(
243*ded733bdSJessica Paquette         inconvertibleErrorCode(),
244*ded733bdSJessica Paquette         Twine("Could not convert string to signed integer: " + ValStr));
245*ded733bdSJessica Paquette   return Val;
246*ded733bdSJessica Paquette }
247*ded733bdSJessica Paquette 
248*ded733bdSJessica Paquette /// Collects relevant size information from \p Remark if it is an size-related
249*ded733bdSJessica Paquette /// remark of some kind (e.g. instruction count). Otherwise records nothing.
250*ded733bdSJessica Paquette ///
251*ded733bdSJessica Paquette /// \p Remark - The remark.
252*ded733bdSJessica Paquette /// \p FuncNameToSizeInfo - Maps function names to relevant size info.
253*ded733bdSJessica Paquette /// \p NumInstCountRemarksParsed - Keeps track of the number of instruction
254*ded733bdSJessica Paquette /// count remarks parsed. We need at least 1 in both files to produce a diff.
255*ded733bdSJessica Paquette static Error processRemark(const remarks::Remark &Remark,
256*ded733bdSJessica Paquette                            StringMap<InstCountAndStackSize> &FuncNameToSizeInfo,
257*ded733bdSJessica Paquette                            unsigned &NumInstCountRemarksParsed) {
258*ded733bdSJessica Paquette   const auto &RemarkName = Remark.RemarkName;
259*ded733bdSJessica Paquette   const auto &PassName = Remark.PassName;
260*ded733bdSJessica Paquette   // Collect remarks which contain the number of instructions in a function.
261*ded733bdSJessica Paquette   if (PassName == "asm-printer" && RemarkName == "InstructionCount") {
262*ded733bdSJessica Paquette     // Expecting the 0-th argument to have the key "NumInstructions" and an
263*ded733bdSJessica Paquette     // integer value.
264*ded733bdSJessica Paquette     auto MaybeInstCount =
265*ded733bdSJessica Paquette         getIntValFromKey(Remark, /*ArgIdx = */ 0, "NumInstructions");
266*ded733bdSJessica Paquette     if (!MaybeInstCount)
267*ded733bdSJessica Paquette       return MaybeInstCount.takeError();
268*ded733bdSJessica Paquette     FuncNameToSizeInfo[Remark.FunctionName].InstCount = *MaybeInstCount;
269*ded733bdSJessica Paquette     ++NumInstCountRemarksParsed;
270*ded733bdSJessica Paquette   }
271*ded733bdSJessica Paquette   // Collect remarks which contain the stack size of a function.
272*ded733bdSJessica Paquette   else if (PassName == "prologepilog" && RemarkName == "StackSize") {
273*ded733bdSJessica Paquette     // Expecting the 0-th argument to have the key "NumStackBytes" and an
274*ded733bdSJessica Paquette     // integer value.
275*ded733bdSJessica Paquette     auto MaybeStackSize =
276*ded733bdSJessica Paquette         getIntValFromKey(Remark, /*ArgIdx = */ 0, "NumStackBytes");
277*ded733bdSJessica Paquette     if (!MaybeStackSize)
278*ded733bdSJessica Paquette       return MaybeStackSize.takeError();
279*ded733bdSJessica Paquette     FuncNameToSizeInfo[Remark.FunctionName].StackSize = *MaybeStackSize;
280*ded733bdSJessica Paquette   }
281*ded733bdSJessica Paquette   // Either we collected a remark, or it's something we don't care about. In
282*ded733bdSJessica Paquette   // both cases, this is a success.
283*ded733bdSJessica Paquette   return Error::success();
284*ded733bdSJessica Paquette }
285*ded733bdSJessica Paquette 
286*ded733bdSJessica Paquette /// Process all of the size-related remarks in a file.
287*ded733bdSJessica Paquette ///
288*ded733bdSJessica Paquette /// \param[in] InputFileName - Name of file to read from.
289*ded733bdSJessica Paquette /// \param[in, out] FuncNameToSizeInfo - Maps function names to relevant
290*ded733bdSJessica Paquette /// size info.
291*ded733bdSJessica Paquette static Error readFileAndProcessRemarks(
292*ded733bdSJessica Paquette     StringRef InputFileName,
293*ded733bdSJessica Paquette     StringMap<InstCountAndStackSize> &FuncNameToSizeInfo) {
294*ded733bdSJessica Paquette   auto Buf = MemoryBuffer::getFile(InputFileName);
295*ded733bdSJessica Paquette   if (auto EC = Buf.getError())
296*ded733bdSJessica Paquette     return createStringError(
297*ded733bdSJessica Paquette         EC, Twine("Cannot open file '" + InputFileName + "': " + EC.message()));
298*ded733bdSJessica Paquette   auto MaybeParser = remarks::createRemarkParserFromMeta(
299*ded733bdSJessica Paquette       ParserFormat == bitstream ? remarks::Format::Bitstream
300*ded733bdSJessica Paquette                                 : remarks::Format::YAML,
301*ded733bdSJessica Paquette       (*Buf)->getBuffer());
302*ded733bdSJessica Paquette   if (!MaybeParser)
303*ded733bdSJessica Paquette     return MaybeParser.takeError();
304*ded733bdSJessica Paquette   auto &Parser = **MaybeParser;
305*ded733bdSJessica Paquette   auto MaybeRemark = Parser.next();
306*ded733bdSJessica Paquette   unsigned NumInstCountRemarksParsed = 0;
307*ded733bdSJessica Paquette   for (; MaybeRemark; MaybeRemark = Parser.next()) {
308*ded733bdSJessica Paquette     if (auto E = processRemark(**MaybeRemark, FuncNameToSizeInfo,
309*ded733bdSJessica Paquette                                NumInstCountRemarksParsed))
310*ded733bdSJessica Paquette       return E;
311*ded733bdSJessica Paquette   }
312*ded733bdSJessica Paquette   auto E = MaybeRemark.takeError();
313*ded733bdSJessica Paquette   if (!E.isA<remarks::EndOfFileError>())
314*ded733bdSJessica Paquette     return E;
315*ded733bdSJessica Paquette   consumeError(std::move(E));
316*ded733bdSJessica Paquette   // We need at least one instruction count remark in each file to produce a
317*ded733bdSJessica Paquette   // meaningful diff.
318*ded733bdSJessica Paquette   if (NumInstCountRemarksParsed == 0)
319*ded733bdSJessica Paquette     return createStringError(
320*ded733bdSJessica Paquette         inconvertibleErrorCode(),
321*ded733bdSJessica Paquette         "File '" + InputFileName +
322*ded733bdSJessica Paquette             "' did not contain any instruction-count remarks!");
323*ded733bdSJessica Paquette   return Error::success();
324*ded733bdSJessica Paquette }
325*ded733bdSJessica Paquette 
326*ded733bdSJessica Paquette /// Wrapper function for readFileAndProcessRemarks which handles errors.
327*ded733bdSJessica Paquette ///
328*ded733bdSJessica Paquette /// \param[in] InputFileName - Name of file to read from.
329*ded733bdSJessica Paquette /// \param[out] FuncNameToSizeInfo - Populated with information from size
330*ded733bdSJessica Paquette /// remarks in the input file.
331*ded733bdSJessica Paquette ///
332*ded733bdSJessica Paquette /// \returns true if readFileAndProcessRemarks returned no errors. False
333*ded733bdSJessica Paquette /// otherwise.
334*ded733bdSJessica Paquette static bool tryReadFileAndProcessRemarks(
335*ded733bdSJessica Paquette     StringRef InputFileName,
336*ded733bdSJessica Paquette     StringMap<InstCountAndStackSize> &FuncNameToSizeInfo) {
337*ded733bdSJessica Paquette   if (Error E = readFileAndProcessRemarks(InputFileName, FuncNameToSizeInfo)) {
338*ded733bdSJessica Paquette     handleAllErrors(std::move(E), [&](const ErrorInfoBase &PE) {
339*ded733bdSJessica Paquette       PE.log(WithColor::error());
340*ded733bdSJessica Paquette       errs() << '\n';
341*ded733bdSJessica Paquette     });
342*ded733bdSJessica Paquette     return false;
343*ded733bdSJessica Paquette   }
344*ded733bdSJessica Paquette   return true;
345*ded733bdSJessica Paquette }
346*ded733bdSJessica Paquette 
347*ded733bdSJessica Paquette /// Populates \p FuncDiffs with the difference between \p
348*ded733bdSJessica Paquette /// FuncNameToSizeInfoA and \p FuncNameToSizeInfoB.
349*ded733bdSJessica Paquette ///
350*ded733bdSJessica Paquette /// \param[in] FuncNameToSizeInfoA - Size info collected from the first
351*ded733bdSJessica Paquette /// remarks file.
352*ded733bdSJessica Paquette /// \param[in] FuncNameToSizeInfoB - Size info collected from
353*ded733bdSJessica Paquette /// the second remarks file.
354*ded733bdSJessica Paquette /// \param[out] D - Filled with the diff between \p FuncNameToSizeInfoA and
355*ded733bdSJessica Paquette /// \p FuncNameToSizeInfoB.
356*ded733bdSJessica Paquette static void
357*ded733bdSJessica Paquette computeDiff(const StringMap<InstCountAndStackSize> &FuncNameToSizeInfoA,
358*ded733bdSJessica Paquette             const StringMap<InstCountAndStackSize> &FuncNameToSizeInfoB,
359*ded733bdSJessica Paquette             DiffsCategorizedByFilesPresent &DiffsByFilesPresent) {
360*ded733bdSJessica Paquette   SmallSet<std::string, 10> FuncNames;
361*ded733bdSJessica Paquette   for (const auto &FuncName : FuncNameToSizeInfoA.keys())
362*ded733bdSJessica Paquette     FuncNames.insert(FuncName.str());
363*ded733bdSJessica Paquette   for (const auto &FuncName : FuncNameToSizeInfoB.keys())
364*ded733bdSJessica Paquette     FuncNames.insert(FuncName.str());
365*ded733bdSJessica Paquette   for (const std::string &FuncName : FuncNames) {
366*ded733bdSJessica Paquette     const auto &SizeInfoA = FuncNameToSizeInfoA.lookup(FuncName);
367*ded733bdSJessica Paquette     const auto &SizeInfoB = FuncNameToSizeInfoB.lookup(FuncName);
368*ded733bdSJessica Paquette     FunctionDiff FuncDiff(FuncName, SizeInfoA, SizeInfoB);
369*ded733bdSJessica Paquette     DiffsByFilesPresent.addDiff(FuncDiff);
370*ded733bdSJessica Paquette   }
371*ded733bdSJessica Paquette }
372*ded733bdSJessica Paquette 
373*ded733bdSJessica Paquette /// Attempt to get the output stream for writing the diff.
374*ded733bdSJessica Paquette static ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() {
375*ded733bdSJessica Paquette   if (OutputFilename == "")
376*ded733bdSJessica Paquette     OutputFilename = "-";
377*ded733bdSJessica Paquette   std::error_code EC;
378*ded733bdSJessica Paquette   auto Out = std::make_unique<ToolOutputFile>(OutputFilename, EC,
379*ded733bdSJessica Paquette                                               sys::fs::OF_TextWithCRLF);
380*ded733bdSJessica Paquette   if (!EC)
381*ded733bdSJessica Paquette     return std::move(Out);
382*ded733bdSJessica Paquette   return EC;
383*ded733bdSJessica Paquette }
384*ded733bdSJessica Paquette 
385*ded733bdSJessica Paquette /// Output all diffs in \p DiffsByFilesPresent.
386*ded733bdSJessica Paquette /// \returns Error::success() on success, and an Error otherwise.
387*ded733bdSJessica Paquette static Error
388*ded733bdSJessica Paquette outputAllDiffs(DiffsCategorizedByFilesPresent &DiffsByFilesPresent) {
389*ded733bdSJessica Paquette   auto MaybeOF = getOutputStream();
390*ded733bdSJessica Paquette   if (std::error_code EC = MaybeOF.getError())
391*ded733bdSJessica Paquette     return errorCodeToError(EC);
392*ded733bdSJessica Paquette   std::unique_ptr<ToolOutputFile> OF = std::move(*MaybeOF);
393*ded733bdSJessica Paquette   printDiffsCategorizedByFilesPresent(DiffsByFilesPresent, OF->os());
394*ded733bdSJessica Paquette   OF->keep();
395*ded733bdSJessica Paquette   return Error::success();
396*ded733bdSJessica Paquette }
397*ded733bdSJessica Paquette 
398*ded733bdSJessica Paquette /// Boolean wrapper for outputDiff which handles errors.
399*ded733bdSJessica Paquette static bool
400*ded733bdSJessica Paquette tryOutputAllDiffs(DiffsCategorizedByFilesPresent &DiffsByFilesPresent) {
401*ded733bdSJessica Paquette   if (Error E = outputAllDiffs(DiffsByFilesPresent)) {
402*ded733bdSJessica Paquette     handleAllErrors(std::move(E), [&](const ErrorInfoBase &PE) {
403*ded733bdSJessica Paquette       PE.log(WithColor::error());
404*ded733bdSJessica Paquette       errs() << '\n';
405*ded733bdSJessica Paquette     });
406*ded733bdSJessica Paquette     return false;
407*ded733bdSJessica Paquette   }
408*ded733bdSJessica Paquette   return true;
409*ded733bdSJessica Paquette }
410*ded733bdSJessica Paquette 
411*ded733bdSJessica Paquette int main(int argc, const char **argv) {
412*ded733bdSJessica Paquette   InitLLVM X(argc, argv);
413*ded733bdSJessica Paquette   cl::HideUnrelatedOptions(SizeDiffCategory);
414*ded733bdSJessica Paquette   cl::ParseCommandLineOptions(argc, argv,
415*ded733bdSJessica Paquette                               "Diff instruction count and stack size remarks "
416*ded733bdSJessica Paquette                               "between two remark files.\n");
417*ded733bdSJessica Paquette   StringMap<InstCountAndStackSize> FuncNameToSizeInfoA;
418*ded733bdSJessica Paquette   StringMap<InstCountAndStackSize> FuncNameToSizeInfoB;
419*ded733bdSJessica Paquette   if (!tryReadFileAndProcessRemarks(InputFileNameA, FuncNameToSizeInfoA) ||
420*ded733bdSJessica Paquette       !tryReadFileAndProcessRemarks(InputFileNameB, FuncNameToSizeInfoB))
421*ded733bdSJessica Paquette     return 1;
422*ded733bdSJessica Paquette   DiffsCategorizedByFilesPresent DiffsByFilesPresent;
423*ded733bdSJessica Paquette   computeDiff(FuncNameToSizeInfoA, FuncNameToSizeInfoB, DiffsByFilesPresent);
424*ded733bdSJessica Paquette   if (!tryOutputAllDiffs(DiffsByFilesPresent))
425*ded733bdSJessica Paquette     return 1;
426*ded733bdSJessica Paquette }
427