//===-------------- RemarkSizeDiff.cpp ------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// /// /// \file /// Diffs instruction count and stack size remarks between two remark files. /// /// This is intended for use by compiler developers who want to see how their /// changes impact program code size. /// //===----------------------------------------------------------------------===// #include "RemarkUtilHelpers.h" #include "RemarkUtilRegistry.h" #include "llvm/ADT/SmallSet.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/JSON.h" usingnamespacellvm; usingnamespaceremarks; usingnamespaceremarkutil; static cl::SubCommand RemarkSizeDiffUtil("size-diff", "Diff instruction count and stack size remarks " "between two remark files"); enum ReportStyleOptions { … }; static cl::opt<std::string> InputFileNameA(cl::Positional, cl::Required, cl::sub(RemarkSizeDiffUtil), cl::desc("remarks_a")); static cl::opt<std::string> InputFileNameB(cl::Positional, cl::Required, cl::sub(RemarkSizeDiffUtil), cl::desc("remarks_b")); static cl::opt<std::string> OutputFilename("o", cl::init("-"), cl::sub(RemarkSizeDiffUtil), cl::desc("Output"), cl::value_desc("file")); INPUT_FORMAT_COMMAND_LINE_OPTIONS(…) static cl::opt<ReportStyleOptions> ReportStyle( "report_style", cl::sub(RemarkSizeDiffUtil), cl::init(ReportStyleOptions::human_output), cl::desc("Choose the report output format:"), cl::values(clEnumValN(human_output, "human", "Human-readable format"), clEnumValN(json_output, "json", "JSON format"))); static cl::opt<bool> PrettyPrint("pretty", cl::sub(RemarkSizeDiffUtil), cl::init(false), cl::desc("Pretty-print JSON")); /// Contains information from size remarks. // This is a little nicer to read than a std::pair. struct InstCountAndStackSize { … }; /// Represents which files a function appeared in. enum FilesPresent { … }; /// Contains the data from the remarks in file A and file B for some function. /// E.g. instruction count, stack size... struct FunctionDiff { … }; /// Organizes the diffs into 3 categories: /// - Functions which only appeared in the first file /// - Functions which only appeared in the second file /// - Functions which appeared in both files struct DiffsCategorizedByFilesPresent { … }; static void printFunctionDiff(const FunctionDiff &FD, llvm::raw_ostream &OS) { … } /// Print an item in the summary section. /// /// \p TotalA - Total count of the metric in file A. /// \p TotalB - Total count of the metric in file B. /// \p Metric - Name of the metric we want to print (e.g. instruction /// count). /// \p OS - The output stream. static void printSummaryItem(int64_t TotalA, int64_t TotalB, StringRef Metric, llvm::raw_ostream &OS) { … } /// Print all contents of \p Diff and a high-level summary of the differences. static void printDiffsCategorizedByFilesPresent( DiffsCategorizedByFilesPresent &DiffsByFilesPresent, llvm::raw_ostream &OS) { … } /// Collects an expected integer value from a given argument index in a remark. /// /// \p Remark - The remark. /// \p ArgIdx - The index where the integer value should be found. /// \p ExpectedKeyName - The expected key name for the index /// (e.g. "InstructionCount") /// /// \returns the integer value at the index if it exists, and the key-value pair /// is what is expected. Otherwise, returns an Error. static Expected<int64_t> getIntValFromKey(const remarks::Remark &Remark, unsigned ArgIdx, StringRef ExpectedKeyName) { … } /// Collects relevant size information from \p Remark if it is an size-related /// remark of some kind (e.g. instruction count). Otherwise records nothing. /// /// \p Remark - The remark. /// \p FuncNameToSizeInfo - Maps function names to relevant size info. /// \p NumInstCountRemarksParsed - Keeps track of the number of instruction /// count remarks parsed. We need at least 1 in both files to produce a diff. static Error processRemark(const remarks::Remark &Remark, StringMap<InstCountAndStackSize> &FuncNameToSizeInfo, unsigned &NumInstCountRemarksParsed) { … } /// Process all of the size-related remarks in a file. /// /// \param[in] InputFileName - Name of file to read from. /// \param[in, out] FuncNameToSizeInfo - Maps function names to relevant /// size info. static Error readFileAndProcessRemarks( StringRef InputFileName, StringMap<InstCountAndStackSize> &FuncNameToSizeInfo) { … } /// Wrapper function for readFileAndProcessRemarks which handles errors. /// /// \param[in] InputFileName - Name of file to read from. /// \param[out] FuncNameToSizeInfo - Populated with information from size /// remarks in the input file. /// /// \returns true if readFileAndProcessRemarks returned no errors. False /// otherwise. static Error tryReadFileAndProcessRemarks( StringRef InputFileName, StringMap<InstCountAndStackSize> &FuncNameToSizeInfo) { … } /// Populates \p FuncDiffs with the difference between \p /// FuncNameToSizeInfoA and \p FuncNameToSizeInfoB. /// /// \param[in] FuncNameToSizeInfoA - Size info collected from the first /// remarks file. /// \param[in] FuncNameToSizeInfoB - Size info collected from /// the second remarks file. /// \param[out] DiffsByFilesPresent - Filled with the diff between \p /// FuncNameToSizeInfoA and \p FuncNameToSizeInfoB. static void computeDiff(const StringMap<InstCountAndStackSize> &FuncNameToSizeInfoA, const StringMap<InstCountAndStackSize> &FuncNameToSizeInfoB, DiffsCategorizedByFilesPresent &DiffsByFilesPresent) { … } /// Attempt to get the output stream for writing the diff. static ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() { … } /// \return a json::Array representing all FunctionDiffs in \p FunctionDiffs. /// \p WhichFiles represents which files the functions in \p FunctionDiffs /// appeared in (A, B, or both). json::Array getFunctionDiffListAsJSON(const SmallVector<FunctionDiff> &FunctionDiffs, const FilesPresent &WhichFiles) { … } /// Output all diffs in \p DiffsByFilesPresent as a JSON report. This is /// intended for consumption by external tools. /// /// \p InputFileNameA - File A used to produce the report. /// \p InputFileNameB - File B used ot produce the report. /// \p OS - Output stream. /// /// JSON output includes: /// - \p InputFileNameA and \p InputFileNameB under "Files". /// - Functions present in both files under "InBoth". /// - Functions present only in A in "OnlyInA". /// - Functions present only in B in "OnlyInB". /// - Instruction count and stack size differences for each function. /// /// Differences are represented using [count_a, count_b]. The actual difference /// can be computed via count_b - count_a. static void outputJSONForAllDiffs(StringRef InputFileNameA, StringRef InputFileNameB, const DiffsCategorizedByFilesPresent &DiffsByFilesPresent, llvm::raw_ostream &OS) { … } /// Output all diffs in \p DiffsByFilesPresent using the desired output style. /// \returns Error::success() on success, and an Error otherwise. /// \p InputFileNameA - Name of input file A; may be used in the report. /// \p InputFileNameB - Name of input file B; may be used in the report. static Error outputAllDiffs(StringRef InputFileNameA, StringRef InputFileNameB, DiffsCategorizedByFilesPresent &DiffsByFilesPresent) { … } /// Boolean wrapper for outputDiff which handles errors. static Error tryOutputAllDiffs(StringRef InputFileNameA, StringRef InputFileNameB, DiffsCategorizedByFilesPresent &DiffsByFilesPresent) { … } static Error trySizeSiff() { … } static CommandRegistration RemarkSizeSiffRegister(&RemarkSizeDiffUtil, trySizeSiff);