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