|  | //===- RemarkInstructionMix.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 | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // Generic tool to extract instruction mix from asm-printer remarks. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "RemarkUtilHelpers.h" | 
|  | #include "RemarkUtilRegistry.h" | 
|  |  | 
|  | #include "llvm/Support/Format.h" | 
|  | #include "llvm/Support/FormattedStream.h" | 
|  | #include "llvm/Support/Regex.h" | 
|  |  | 
|  | #include <cmath> | 
|  | #include <numeric> | 
|  |  | 
|  | using namespace llvm; | 
|  | using namespace remarks; | 
|  | using namespace llvm::remarkutil; | 
|  |  | 
|  | namespace instructionmix { | 
|  |  | 
|  | static cl::SubCommand | 
|  | InstructionMix("instruction-mix", | 
|  | "Instruction Mix (requires asm-printer remarks)"); | 
|  |  | 
|  | static cl::opt<std::string> | 
|  | FunctionFilter("filter", cl::sub(InstructionMix), cl::ValueOptional, | 
|  | cl::desc("Optional function name to filter collection by")); | 
|  |  | 
|  | static cl::opt<std::string> | 
|  | FunctionFilterRE("rfilter", cl::sub(InstructionMix), cl::ValueOptional, | 
|  | cl::desc("Optional function name to filter collection by " | 
|  | "(accepts regular expressions)")); | 
|  |  | 
|  | enum ReportStyleOptions { human_output, csv_output }; | 
|  | static cl::opt<ReportStyleOptions> ReportStyle( | 
|  | "report_style", cl::sub(InstructionMix), | 
|  | cl::init(ReportStyleOptions::human_output), | 
|  | cl::desc("Choose the report output format:"), | 
|  | cl::values(clEnumValN(human_output, "human", "Human-readable format"), | 
|  | clEnumValN(csv_output, "csv", "CSV format"))); | 
|  |  | 
|  | INPUT_FORMAT_COMMAND_LINE_OPTIONS(InstructionMix) | 
|  | INPUT_OUTPUT_COMMAND_LINE_OPTIONS(InstructionMix) | 
|  |  | 
|  | static Error tryInstructionMix() { | 
|  | auto MaybeOF = | 
|  | getOutputFileWithFlags(OutputFileName, sys::fs::OF_TextWithCRLF); | 
|  | if (!MaybeOF) | 
|  | return MaybeOF.takeError(); | 
|  |  | 
|  | auto OF = std::move(*MaybeOF); | 
|  | auto MaybeBuf = getInputMemoryBuffer(InputFileName); | 
|  | if (!MaybeBuf) | 
|  | return MaybeBuf.takeError(); | 
|  | auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer()); | 
|  | if (!MaybeParser) | 
|  | return MaybeParser.takeError(); | 
|  |  | 
|  | Expected<std::optional<FilterMatcher>> Filter = | 
|  | FilterMatcher::createExactOrRE(FunctionFilter, FunctionFilterRE); | 
|  | if (!Filter) | 
|  | return Filter.takeError(); | 
|  |  | 
|  | // Collect the histogram of instruction counts. | 
|  | llvm::DenseMap<StringRef, unsigned> Histogram; | 
|  | auto &Parser = **MaybeParser; | 
|  | auto MaybeRemark = Parser.next(); | 
|  | for (; MaybeRemark; MaybeRemark = Parser.next()) { | 
|  | Remark &Remark = **MaybeRemark; | 
|  | if (Remark.RemarkName != "InstructionMix") | 
|  | continue; | 
|  | if (*Filter && !(*Filter)->match(Remark.FunctionName)) | 
|  | continue; | 
|  | for (auto &Arg : Remark.Args) { | 
|  | StringRef Key = Arg.Key; | 
|  | if (!Key.consume_front("INST_")) | 
|  | continue; | 
|  | unsigned Val = 0; | 
|  | bool ParseError = Arg.Val.getAsInteger(10, Val); | 
|  | assert(!ParseError); | 
|  | (void)ParseError; | 
|  | Histogram[Key] += Val; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Sort it. | 
|  | using MixEntry = std::pair<StringRef, unsigned>; | 
|  | llvm::SmallVector<MixEntry> Mix(Histogram.begin(), Histogram.end()); | 
|  | std::sort(Mix.begin(), Mix.end(), [](const auto &LHS, const auto &RHS) { | 
|  | return LHS.second > RHS.second; | 
|  | }); | 
|  |  | 
|  | // Print the results. | 
|  | switch (ReportStyle) { | 
|  | case human_output: { | 
|  | formatted_raw_ostream FOS(OF->os()); | 
|  | size_t MaxMnemonic = | 
|  | std::accumulate(Mix.begin(), Mix.end(), StringRef("Instruction").size(), | 
|  | [](size_t MaxMnemonic, const MixEntry &Elt) { | 
|  | return std::max(MaxMnemonic, Elt.first.size()); | 
|  | }); | 
|  | unsigned MaxValue = std::accumulate( | 
|  | Mix.begin(), Mix.end(), 1, [](unsigned MaxValue, const MixEntry &Elt) { | 
|  | return std::max(MaxValue, Elt.second); | 
|  | }); | 
|  | unsigned ValueWidth = std::log10(MaxValue) + 1; | 
|  | FOS << "Instruction"; | 
|  | FOS.PadToColumn(MaxMnemonic + 1) << "Count\n"; | 
|  | FOS << "-----------"; | 
|  | FOS.PadToColumn(MaxMnemonic + 1) << "-----\n"; | 
|  | for (const auto &[Inst, Count] : Mix) { | 
|  | FOS << Inst; | 
|  | FOS.PadToColumn(MaxMnemonic + 1) | 
|  | << " " << format_decimal(Count, ValueWidth) << "\n"; | 
|  | } | 
|  | } break; | 
|  | case csv_output: { | 
|  | OF->os() << "Instruction,Count\n"; | 
|  | for (const auto &[Inst, Count] : Mix) | 
|  | OF->os() << Inst << "," << Count << "\n"; | 
|  | } break; | 
|  | } | 
|  |  | 
|  | auto E = MaybeRemark.takeError(); | 
|  | if (!E.isA<EndOfFileError>()) | 
|  | return E; | 
|  | consumeError(std::move(E)); | 
|  | OF->keep(); | 
|  | return Error::success(); | 
|  | } | 
|  |  | 
|  | static CommandRegistration InstructionMixReg(&InstructionMix, | 
|  | tryInstructionMix); | 
|  |  | 
|  | } // namespace instructionmix |