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 | |