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. 138eb9ac2ccSSimon Pilgrim FilesPresent FP = FD.getFilesPresent(); 139eb9ac2ccSSimon 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. 144eb9ac2ccSSimon 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. 354*16655a58SSimon Pilgrim /// \param[out] DiffsByFilesPresent - Filled with the diff between \p 355*16655a58SSimon Pilgrim /// FuncNameToSizeInfoA and \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