| 1 | //===- RemarkInstructionMix.cpp -------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // Generic tool to extract instruction mix from asm-printer remarks. |
| 10 | // |
| 11 | //===----------------------------------------------------------------------===// |
| 12 | |
| 13 | #include "RemarkUtilHelpers.h" |
| 14 | #include "RemarkUtilRegistry.h" |
| 15 | |
| 16 | #include "llvm/Support/Format.h" |
| 17 | #include "llvm/Support/FormattedStream.h" |
| 18 | #include "llvm/Support/Regex.h" |
| 19 | |
| 20 | #include <cmath> |
| 21 | #include <numeric> |
| 22 | |
| 23 | using namespace llvm; |
| 24 | using namespace remarks; |
| 25 | using namespace llvm::remarkutil; |
| 26 | |
| 27 | namespace instructionmix { |
| 28 | |
| 29 | static cl::SubCommand |
| 30 | InstructionMix("instruction-mix" , |
| 31 | "Instruction Mix (requires asm-printer remarks)" ); |
| 32 | |
| 33 | static cl::opt<std::string> |
| 34 | FunctionFilter("filter" , cl::sub(InstructionMix), cl::ValueOptional, |
| 35 | cl::desc("Optional function name to filter collection by" )); |
| 36 | |
| 37 | static cl::opt<std::string> |
| 38 | FunctionFilterRE("rfilter" , cl::sub(InstructionMix), cl::ValueOptional, |
| 39 | cl::desc("Optional function name to filter collection by " |
| 40 | "(accepts regular expressions)" )); |
| 41 | |
| 42 | enum ReportStyleOptions { human_output, csv_output }; |
| 43 | static cl::opt<ReportStyleOptions> ReportStyle( |
| 44 | "report_style" , cl::sub(InstructionMix), |
| 45 | cl::init(Val: ReportStyleOptions::human_output), |
| 46 | cl::desc("Choose the report output format:" ), |
| 47 | cl::values(clEnumValN(human_output, "human" , "Human-readable format" ), |
| 48 | clEnumValN(csv_output, "csv" , "CSV format" ))); |
| 49 | |
| 50 | INPUT_FORMAT_COMMAND_LINE_OPTIONS(InstructionMix) |
| 51 | INPUT_OUTPUT_COMMAND_LINE_OPTIONS(InstructionMix) |
| 52 | |
| 53 | static Error tryInstructionMix() { |
| 54 | auto MaybeOF = |
| 55 | getOutputFileWithFlags(OutputFileName, Flags: sys::fs::OF_TextWithCRLF); |
| 56 | if (!MaybeOF) |
| 57 | return MaybeOF.takeError(); |
| 58 | |
| 59 | auto OF = std::move(*MaybeOF); |
| 60 | auto MaybeBuf = getInputMemoryBuffer(InputFileName); |
| 61 | if (!MaybeBuf) |
| 62 | return MaybeBuf.takeError(); |
| 63 | auto MaybeParser = createRemarkParser(ParserFormat: InputFormat, Buf: (*MaybeBuf)->getBuffer()); |
| 64 | if (!MaybeParser) |
| 65 | return MaybeParser.takeError(); |
| 66 | |
| 67 | Expected<std::optional<FilterMatcher>> Filter = |
| 68 | FilterMatcher::createExactOrRE(ExactArg: FunctionFilter, REArg: FunctionFilterRE); |
| 69 | if (!Filter) |
| 70 | return Filter.takeError(); |
| 71 | |
| 72 | // Collect the histogram of instruction counts. |
| 73 | llvm::DenseMap<StringRef, unsigned> Histogram; |
| 74 | auto &Parser = **MaybeParser; |
| 75 | auto = Parser.next(); |
| 76 | for (; MaybeRemark; MaybeRemark = Parser.next()) { |
| 77 | Remark & = **MaybeRemark; |
| 78 | if (Remark.RemarkName != "InstructionMix" ) |
| 79 | continue; |
| 80 | if (*Filter && !(*Filter)->match(StringToMatch: Remark.FunctionName)) |
| 81 | continue; |
| 82 | for (auto &Arg : Remark.Args) { |
| 83 | StringRef Key = Arg.Key; |
| 84 | if (!Key.consume_front(Prefix: "INST_" )) |
| 85 | continue; |
| 86 | unsigned Val = 0; |
| 87 | bool ParseError = Arg.Val.getAsInteger(Radix: 10, Result&: Val); |
| 88 | assert(!ParseError); |
| 89 | (void)ParseError; |
| 90 | Histogram[Key] += Val; |
| 91 | } |
| 92 | } |
| 93 | |
| 94 | // Sort it. |
| 95 | using MixEntry = std::pair<StringRef, unsigned>; |
| 96 | llvm::SmallVector<MixEntry> Mix(Histogram.begin(), Histogram.end()); |
| 97 | std::sort(first: Mix.begin(), last: Mix.end(), comp: [](const auto &LHS, const auto &RHS) { |
| 98 | return LHS.second > RHS.second; |
| 99 | }); |
| 100 | |
| 101 | // Print the results. |
| 102 | switch (ReportStyle) { |
| 103 | case human_output: { |
| 104 | formatted_raw_ostream FOS(OF->os()); |
| 105 | size_t MaxMnemonic = |
| 106 | std::accumulate(first: Mix.begin(), last: Mix.end(), init: StringRef("Instruction" ).size(), |
| 107 | binary_op: [](size_t MaxMnemonic, const MixEntry &Elt) { |
| 108 | return std::max(a: MaxMnemonic, b: Elt.first.size()); |
| 109 | }); |
| 110 | unsigned MaxValue = std::accumulate( |
| 111 | first: Mix.begin(), last: Mix.end(), init: 1, binary_op: [](unsigned MaxValue, const MixEntry &Elt) { |
| 112 | return std::max(a: MaxValue, b: Elt.second); |
| 113 | }); |
| 114 | unsigned ValueWidth = std::log10(x: MaxValue) + 1; |
| 115 | FOS << "Instruction" ; |
| 116 | FOS.PadToColumn(NewCol: MaxMnemonic + 1) << "Count\n" ; |
| 117 | FOS << "-----------" ; |
| 118 | FOS.PadToColumn(NewCol: MaxMnemonic + 1) << "-----\n" ; |
| 119 | for (const auto &[Inst, Count] : Mix) { |
| 120 | FOS << Inst; |
| 121 | FOS.PadToColumn(NewCol: MaxMnemonic + 1) |
| 122 | << " " << format_decimal(N: Count, Width: ValueWidth) << "\n" ; |
| 123 | } |
| 124 | } break; |
| 125 | case csv_output: { |
| 126 | OF->os() << "Instruction,Count\n" ; |
| 127 | for (const auto &[Inst, Count] : Mix) |
| 128 | OF->os() << Inst << "," << Count << "\n" ; |
| 129 | } break; |
| 130 | } |
| 131 | |
| 132 | auto E = MaybeRemark.takeError(); |
| 133 | if (!E.isA<EndOfFileError>()) |
| 134 | return E; |
| 135 | consumeError(Err: std::move(E)); |
| 136 | OF->keep(); |
| 137 | return Error::success(); |
| 138 | } |
| 139 | |
| 140 | static CommandRegistration InstructionMixReg(&InstructionMix, |
| 141 | tryInstructionMix); |
| 142 | |
| 143 | } // namespace instructionmix |
| 144 | |