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