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
23using namespace llvm;
24using namespace remarks;
25using namespace llvm::remarkutil;
26
27namespace instructionmix {
28
29static cl::SubCommand
30 InstructionMix("instruction-mix",
31 "Instruction Mix (requires asm-printer remarks)");
32
33static cl::opt<std::string>
34 FunctionFilter("filter", cl::sub(InstructionMix), cl::ValueOptional,
35 cl::desc("Optional function name to filter collection by"));
36
37static 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
42enum ReportStyleOptions { human_output, csv_output };
43static 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
50INPUT_FORMAT_COMMAND_LINE_OPTIONS(InstructionMix)
51INPUT_OUTPUT_COMMAND_LINE_OPTIONS(InstructionMix)
52
53static 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 MaybeRemark = Parser.next();
76 for (; MaybeRemark; MaybeRemark = Parser.next()) {
77 Remark &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
140static CommandRegistration InstructionMixReg(&InstructionMix,
141 tryInstructionMix);
142
143} // namespace instructionmix
144